package refobjects

import (
	"sort"
	"sync"
	"time"
)

type ArgsFunc = func(args ...interface{})

type SyncFuncList struct {
	lk  sync.RWMutex
	lst map[any]*funitem
}

type funitem struct {
	id   any
	fn   ArgsFunc
	args []interface{}
	t    time.Time
}

func NewSyncFuncList() *SyncFuncList {
	return &SyncFuncList{lst: make(map[any]*funitem)}
}

func (this *SyncFuncList) AddOrSet(id any, fn ArgsFunc, args ...interface{}) {
	this.lk.Lock()
	defer this.lk.Unlock()
	if itm, ok := this.lst[id]; ok {
		itm.fn = fn
		itm.args = args
	} else {
		itm = &funitem{id: id, fn: fn, args: args, t: time.Now()}
		this.lst[id] = itm
	}
	return
}

func (this *SyncFuncList) Remove(id any) bool {
	this.lk.Lock()
	defer this.lk.Unlock()
	if itm, ok := this.lst[id]; ok {
		itm.fn = nil
		itm.args = nil
		delete(this.lst, id)
		return true
	}
	return false
}

func (this *SyncFuncList) Clear() {
	this.lk.Lock()
	defer this.lk.Unlock()
	for k, itm := range this.lst {
		itm.fn = nil
		itm.args = nil
		delete(this.lst, k)
	}
}

func (this *SyncFuncList) Execute(id any) bool {
	var itm funitem
	this.lk.RLock()
	p := this.lst[id]
	if p != nil {
		itm = *p
	}
	this.lk.RUnlock()
	if p == nil {
		return false
	}

	itm.fn(itm.args...)
	itm.fn = nil
	itm.args = nil
	return true
}

func (this *SyncFuncList) ExecuteWithArgsAndRemove(id any, args ...interface{}) bool {
	var itm *funitem
	this.lk.Lock()
	p := this.lst[id]
	if p != nil {
		itm = p
	}
	delete(this.lst, id)
	this.lk.Unlock()
	if p == nil {
		return false
	}

	itm.fn(args...)
	itm.fn = nil
	itm.args = nil
	return true
}

func (this *SyncFuncList) ExecuteAndRemove(id any) bool {
	var itm *funitem
	this.lk.Lock()
	p := this.lst[id]
	if p != nil {
		itm = p
	}
	delete(this.lst, id)
	this.lk.Unlock()
	if p == nil {
		return false
	}

	itm.fn(itm.args...)
	itm.fn = nil
	itm.args = nil
	return true
}

func (this *SyncFuncList) ExecuteAndRemoveAll() int {
	this.lk.Lock()
	var lst []*funitem = make([]*funitem, 0, len(this.lst))
	for k, v := range this.lst {
		lst = append(lst, v)
		delete(this.lst, k)
	}
	this.lk.Unlock()

	sort.Slice(lst, func(i, j int) bool {
		return lst[i].t.After(lst[j].t)
	})

	for i := 0; i < len(lst); i++ {
		itm := lst[i]
		lst[i].fn(lst[i].args...)
		itm.fn = nil
		itm.args = nil
	}
	return len(lst)
}

func (this *SyncFuncList) ExecuteWithArgs(id any, args ...interface{}) bool {
	var itm funitem
	this.lk.RLock()
	p := this.lst[id]
	if p != nil {
		itm = *p
	}
	this.lk.RUnlock()
	if p == nil {
		return false
	}

	itm.fn(args...)
	itm.fn = nil
	itm.args = nil
	return true
}

func (this *SyncFuncList) ExecuteAllWithArgs(args ...interface{}) int {
	this.lk.RLock()
	var lst []*funitem = make([]*funitem, 0, len(this.lst))
	for _, v := range this.lst {
		itm := *v // copy all
		itm.args = args
		lst = append(lst, &itm)
	}
	this.lk.RUnlock()

	sort.Slice(lst, func(i, j int) bool {
		return lst[i].t.After(lst[j].t)
	})

	for i := 0; i < len(lst); i++ {
		itm := lst[i]
		lst[i].fn(lst[i].args...)
		itm.fn = nil
		itm.args = nil
	}
	return len(lst)
}

func (this *SyncFuncList) ExecuteFuncs() int {
	this.lk.RLock()
	var lst []*funitem = make([]*funitem, 0, len(this.lst))
	for _, v := range this.lst {
		itm := *v // copy all
		lst = append(lst, &itm)
	}
	this.lk.RUnlock()

	sort.Slice(lst, func(i, j int) bool {
		return lst[i].t.After(lst[j].t)
	})

	for i := 0; i < len(lst); i++ {
		itm := lst[i]
		lst[i].fn(lst[i].args...)
		itm.fn = nil
		itm.args = nil
	}
	return len(lst)
}
